﻿//LevelQuotingStrategy.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Strategies
{
	using System;
	using System.Linq;

	using Ecng.ComponentModel;

	using StockSharp.BusinessEntities;

	/// <summary>
	/// Котирование по заданному уровню в стакане.
	/// </summary>
	public class LevelQuotingStrategy : QuotingStrategy
	{
		/// <summary>
		/// Создать <see cref="LevelQuotingStrategy"/>.
		/// </summary>
		/// <param name="quotingDirection">Направление котирования.</param>
		/// <param name="quotingVolume">Объем, который необходимо скотировать.</param>
		public LevelQuotingStrategy(OrderDirections quotingDirection, decimal quotingVolume)
			: base(quotingDirection, quotingVolume)
		{
			_level = this.Param("Level", new Range<int>());
			_ownLevel = this.Param<bool>("OwnLevel");
		}

		private readonly StrategyParam<Range<int>> _level;

		/// <summary>
		/// Уровень в стакане. Определяет количество котировок вглубь стакана от лучшей. По-умолчанию равно 0, что означает котирование по лучшей котировке.
		/// </summary>
		public Range<int> Level
		{
			get { return _level.Value; }
			set
			{
				if (value == null)
					throw new ArgumentNullException();

				if (value.Contains(-1))
					throw new ArgumentOutOfRangeException();

				if (value == Level)
					return;

				_level.Value = value;
			}
		}

		private readonly StrategyParam<bool> _ownLevel;

		/// <summary>
		/// Создавать свой собственный ценовой уровень в стакане, если котировки с необходимой ценой еще нет. По-умолчанию выключено.
		/// </summary>
		public bool OwnLevel
		{
			get { return _ownLevel.Value; }
			set { _ownLevel.Value = value; }
		}

		/// <summary>
		/// Получить приемлемый диапазон цены, попадая в который текущая заявка не будет перерегистрирована.
		/// </summary>
		/// <returns>Приемлемый диапазон цены. Если невозможно вычислить диапазон на данный момент, то будет возвращено null.</returns>
		protected override Range<decimal> GetAcceptablePriceRange()
		{
			var quotes = FilteredQuotes;

			var from = quotes.ElementAtOrDefault(Level.Min);

			if (from == null)
				return null;
			else
			{
				var to = quotes.ElementAtOrDefault(Level.Max);

				decimal toPrice;

				if (to == null)
					toPrice = OwnLevel ? (decimal)(from.Price + Level.Length.Pips(Security)) : quotes.Last().Price;
				else
					toPrice = to.Price;

				return QuotingDirection == OrderDirections.Sell
					? new Range<decimal>(from.Price, toPrice)
					: new Range<decimal>(toPrice, from.Price);
			}
		}

		/// <summary>
		/// Получить цену для выставляемой заявки.
		/// </summary>
		/// <param name="acceptablePriceRange">Приемлемый диапазон цены.</param>
		/// <returns>Цена, по которой будет выставлена заявка. Если возвращается 0, то заявка выставляться не будет.</returns>
		protected override decimal GetOrderPrice(Range<decimal> acceptablePriceRange)
		{
			return acceptablePriceRange.Min + (acceptablePriceRange.Length / 2);
		}
	}
}